package cn.com.client;

import cn.com.cache.CacheFactory;
import cn.com.cache.ZrCache;
import cn.com.client.send.RecordAccumulator;
import cn.com.common.Constant;
import cn.com.common.model.CacheValueModel;

/**
 * @author jiaming
 */
public class ZrStore {

    private static final RecordAccumulator recordAccumulator = new RecordAccumulator();

    /**
     * 该方法会返回该key是否是热key，如果是返回true，如果不是返回false，并且会将key
     * 上报到探测集群进行数量计算。该方法通常用于判断只需要判断key是否热、不需要缓存value的场景，
     * 如刷子用户、接口访问频率等。
     *
     * @param key
     * @return is hotkey return true else false
     */
    public static boolean isHotKey(String key) {
        //先从本地判断是否是hotkey，如果不是则发往server检测
        //验证是否是在探测规则内
        if (!isInRule(key)) {
            return false;
        }
        //判断是否是hotkey
        CacheValueModel value = getCacheValueModel(key);
        if (value == null) {
            //不是hotkey 发完server端做检测
            recordAccumulator.append(key);
            return false;
        } else {
            //验证是否即将过期，如果过期则发往server做检测
            if (isNearExpiration(value)) {
                //发往server做检测
                recordAccumulator.append(key);
            }
        }
        return true;
    }

    /**
     * 验证value是否即将过期
     *
     * @return true or false
     */
    private static boolean isNearExpiration(CacheValueModel value) {
        long valueCreateTime = value.getCreateTime();
        long currentTimeMillis = System.currentTimeMillis();
        long nearExpiration = currentTimeMillis - (valueCreateTime + value.getExpire());
        return nearExpiration < Constant.NEAR_EXPIRATION_THRESHOLD;
    }

    /**
     * 校验key是否在规则内
     *
     * @param key
     * @return
     */
    private static boolean isInRule(String key) {
        ZrCache zrCache = CacheFactory.getCache(key);
        return zrCache != null;
    }

    /**
     * 该方法返回该key本地缓存的value值，可用于判断是热key后，
     * 再去获取本地缓存的value值，通常用于redis热key缓存
     *
     * @param key
     * @return value
     */
    public static Object get(String key) {
        //判断是否是hotkey
        CacheValueModel cacheValueModel = getCacheValueModel(key);
        if (cacheValueModel == null) {
            //不是hotkey
            return null;
        }
        return cacheValueModel.getValue();
    }

    /**
     * 方法给热key赋值value，如果是热key，该方法才会赋值，非热key，什么也不做
     *
     * @param key
     * @param value
     */
    public static void setHotKeyValue(String key, Object value) {
        if (isHot(key)) {
            CacheValueModel cacheValueModel = getCacheValueModel(key);
            if (cacheValueModel == null) {
                return;
            }
            cacheValueModel.setValue(value);
        }
    }

    private static boolean isHot(String key) {
        //判断是否是hotkey
        CacheValueModel value = getCacheValueModel(key);
        return value != null;
    }

    private static CacheValueModel getCacheValueModel(String key) {
        ZrCache cache = CacheFactory.getNonNullCache(key);
        return (CacheValueModel) cache.get(key);
    }

    /**
     * 该方法是一个整合方法，相当于isHotKey和get两个方法的整合，
     * 该方法直接返回本地缓存的value。 如果是热key，则存在两种情况：
     * <p>
     * 1、是返回value
     * 2、是返回null
     * <p>
     * 返回null是因为尚未给它set真正的value，
     * 返回非null说明已经调用过set方法了，本地缓存value有值了。
     * 如果不是热key则返回null，并且将key上报到探测集群进行数量探测。
     *
     * @param key
     * @return value
     */
    public static Object getValue(String key) {
        //验证是否在规则内，如果不是则返回null
        if (!isInRule(key)) {
            return null;
        }
        if (isHot(key)) {
            CacheValueModel cacheValueModel = getCacheValueModel(key);
            if (cacheValueModel == null) {
                //发送探测
            } else {
                //判断是否即将过期
                if (isNearExpiration(cacheValueModel)) {
                    //发送探测
                    recordAccumulator.append(key);
                }
            }
            return cacheValueModel.getValue();
        } else {
            //key上传server探测
            recordAccumulator.append(key);
        }
        return null;
    }

}